import os
import time
import thread
import socket
import struct
import sys

_GOODBYE_MESSAGE = 'Goodbye'
_PING = 'PING'
_PONG = 'PONG'

_INIT = -1
_CONNECTING = 0
_OPEN = 1
_CLOSING = 2
_CLOSED = 3

_MCAST_ADDR = "224.0.2.1"
_MCAST_PORT = 5003
_TIME_OUT = 1.0

gReadyState = _INIT

"""
 Functions for multicast setup

 ref) http://www.huque.com/software/mctester/mctester.src.txt
"""
def multicast_recvsock(group_address, group_port, local_if):
    """create socket for receiving multicast"""
    ip_mreq = socket.inet_aton(group_address) + socket.inet_aton(local_if)

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    if hasattr(socket, 'SO_REUSEPORT'):
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
        
    s.bind((local_if, group_port))
    s.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ip_mreq)
    s.settimeout(_TIME_OUT)

    return s


def multicast_sendsock(ttl=1, loop=1):
    """create socket for sending multicast"""
    packed_ttl  = struct.pack(">B", ttl)
    packed_loop = struct.pack(">B", loop)

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, packed_ttl)
    s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, packed_loop)

    return s

"""
 Multicast Receiver class

 receive locally loopback multicast message from other member and forward to.
"""
class McastReceiver():
    ########################################
    # constructor
    ########################################
    def __init__(self, opt_req):
        self.req = opt_req # request object

        # setup multicast receiver
        ####################################
        try:
          self.sock = multicast_recvsock(_MCAST_ADDR, _MCAST_PORT, "0.0.0.0")
        except:
          raise
            
        
    #########################################
    # run receiver
    #########################################
    def run(self):
        global gReadyState # indicating current state
        
        while True:
            # close
            if gReadyState != _OPEN:
                gReadyState = _CLOSED
                print "close receiver"
                break
            
            try:
                # receive and forward
                data, addr = self.sock.recvfrom(65535)
                self.req.ws_stream.send_message(data.decode('utf-8'))
            except:
                # case exception (timeout)
                continue

        
"""
 Main

"""


# WS handshake
###########################
def web_socket_do_extra_handshake(request):
    pass  # Always accept.

# handle WS frame
###########################
def web_socket_transfer_data(request):
    global gReadyState
    gReadyState = _OPEN # change state to _OPEN

    try:
        # setup multicast sender
        s = multicast_sendsock()

        # start multicast receiver thread
        thread.start_new_thread(McastReceiver(request).run, ())
    except:
        raise

    # handling WS frame
    while True:
        # wait to receive WS frame from user
        line = request.ws_stream.receive_message().encode('utf-8')

        # keep alive packet
        if line == _PING:
            request.ws_stream.send_message(_PONG)
            continue

        # closing Frame arrived?
        if line is None:
            gReadyState = _CLOSING
            time.sleep(_TIME_OUT + 1)
            return
        
        # send multicast packet to other process.
        s.sendto(line, (_MCAST_ADDR, _MCAST_PORT))

        if line == _GOODBYE_MESSAGE:
            gReadyState = _CLOSING
            time.sleep(_TIME_OUT + 1)
            return


